package net.minecraft.src;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

public class TileController extends TileEntity
    implements IInventory
{
		static int sizeOfInputOutput = 4;
		static  int  sizeOfJump = 5;  
		static  int sizeOfFrequency = 4;
		private ItemStack sequencerVisibleContent[];

	    Line[]  lines;
	    FrequencySection frequencySection;
	class ItemStackOperable
	{
		protected boolean isSet( ItemStack stack )
		{
			if (stack == null )
				return  false;
			
			return stack.itemID ==  Item.redstone.shiftedIndex;
		}

		protected boolean isRedTorchSet( ItemStack stack )
		{
			if (stack == null )
				return  false;
			
			return    stack.itemID ==  Block.torchRedstoneIdle.blockID
				   || stack.itemID ==  Block.torchRedstoneActive.blockID;
		}		
		
		public ItemStack  decrementStackSize( int index, int amount,ItemStack [] array )
		{
			ItemStack itemStack;  
			if( array[index].stackSize  <= amount )
			{
				itemStack = array[index]; 
				array[index] = null;
				onInventoryChanged();
				return itemStack;
			}
			itemStack = array[index].splitStack(amount); 
	        if (array[index].stackSize == 0)
	        {
	        	array[index] = null;
	        }	
	        onInventoryChanged();
	        return itemStack;
		}
		
	};

	class InOutInstruction  extends ItemStackOperable
	{
		ItemStack inOut[];

		InOutInstruction()
		{
			inOut = new ItemStack[sizeOfInputOutput];
		}
		
		
		public ItemStack  decrementStackSize( int index, int amount )
		{
			return super.decrementStackSize( index, amount, inOut );
		}
		
		
		public int  getInOut()
		{
			int inOutCode = 0;
			for ( int i = 0; i < sizeOfInputOutput ; ++i )
			{
				inOutCode |=  ( isSet( inOut[ i ] )? 1 : 0  ) << i; 	
			}
			return inOutCode;
		}

		public int  getMask()
		{
			int inOutMask = 0;
			for ( int i = 0; i < sizeOfInputOutput ; ++i )
			{
				inOutMask |=  ( isRedTorchSet( inOut[ i ] )? 1 : 0  ) << i; 	
			}
			return inOutMask;
			
		}
		
		public   int getSize()
		{
			return  sizeOfInputOutput;
		}
		public void setItemStack( int index,ItemStack itemstack  )
		{
			assert(getSize() > index);
			inOut[ index ]=itemstack ;
			
		}
		public ItemStack getItemStack( int index )
		{
			return inOut[ index ];
		}
	};
	class FrequencySection extends ItemStackOperable
	{
		private ItemStack frequency[ ];
		
		FrequencySection()
		{
			frequency = new ItemStack[sizeOfFrequency];
			
		}
		
		public ItemStack  decrementStackSize( int index, int amount )
		{
			return super.decrementStackSize( index, amount, frequency );
		}
		public   int getSize()
		{
			return  sizeOfFrequency;
		}

		public void setItemStack( int index,ItemStack itemstack  )
		{
			assert(getSize() > index);
			frequency[ index ]=itemstack ;
			
		}
		
		public ItemStack getItemStack( int index )
		{
			return frequency[ index ];
		}		
		
	}
	class JumpInstruction  extends ItemStackOperable
	{
		private ItemStack jump[ ];

		
		JumpInstruction()
		{
			jump = new ItemStack[sizeOfJump];
			
		}
		public ItemStack  decrementStackSize( int index, int amount )
		{
			return super.decrementStackSize( index, amount, jump );
		}	
		public int getAddress()
		{
			int address = 0;
			for ( int i = 0; i < sizeOfJump ; ++i )
			{
				address |=  ( isSet( jump[ i ] )? 1 : 0  ) << (sizeOfJump - i -1); 	
			}
			return address;
		}

		public boolean  isEmpty()
		{
			
			for( int i = 0; i < sizeOfJump ; ++i )
			{
				if ( isSet( jump[ i ] ) )
					return false;
			}
			return  true;
		}

		public   int getSize()
		{
			return  sizeOfJump;
		}

		public void setItemStack( int index,ItemStack itemstack  )
		{
			assert(getSize() > index);
			jump[ index ]=itemstack ;
			
		}
		
		public ItemStack getItemStack( int index )
		{
			return jump[ index ];
		}
	};

	class Line {		
		private  JumpInstruction  jumpInstruction;
		private  InOutInstruction inOutInstruction;
/*
		public ItemStack getItemStack( int index )
		{
			
			//if( index < inOutInstruction.getSize())

		//	else

		}
	*/	
		public ItemStack  decrementStackSize( int column, int amount )
		{
			assert( column > 0 &&  column <  jumpInstruction.getSize() );
			if( inOutInstruction.getSize() > column )
			{
				return inOutInstruction.decrementStackSize(column, amount);
			}
			else
			{
				return jumpInstruction.decrementStackSize(column - inOutInstruction.getSize(), amount);
				
			}
			
		}	
		
		public  Line ()
		{
			jumpInstruction  =  new JumpInstruction();
			inOutInstruction = new InOutInstruction();
			
		}
		
		private boolean isJump(){
			
			
			return !jumpInstruction.isEmpty();
		}
		
		public  int  getLineSize()
		{
			return sizeOfInputOutput + sizeOfJump;  // jumpInstruction.getSize() + inOutInstruction.getSize();
		}

		public  void  setStack(int column,ItemStack itemstack )
		{
			assert( column > 0 &&  column <  jumpInstruction.getSize() );
			if( inOutInstruction.getSize() > column )
			{
				inOutInstruction.setItemStack(column, itemstack);
			}
			else
			{
				jumpInstruction.setItemStack(column - inOutInstruction.getSize(), itemstack);
				
			}
		}
		
		public  ItemStack getStack(int column)
		{
			assert( column > 0 &&  column <  jumpInstruction.getSize() );
			if( inOutInstruction.getSize() > column )
			{
				return inOutInstruction.getItemStack(column);
			}
			else
			{
				return jumpInstruction.getItemStack(column - inOutInstruction.getSize());	
				
			}
		}		
		
		public boolean [] execute( int poweringCondition )
		{
			if( isJump() )
			{
				int  mask = inOutInstruction.getMask();
				if(  (poweringCondition | mask) == (inOutInstruction.getInOut()| mask) )
				{
					
					currentSequence = jumpInstruction.getAddress();
				}
				else
				{
					currentSequence++;
				}
				return null;
			}
			else
			{
				int  inOut = inOutInstruction.getInOut();
				boolean [] output= new boolean[ inOutInstruction.getSize() ]; 
				for( int i=0 ;i < inOutInstruction.getSize(); ++i )
				{
					output[ i ]  = ( (inOut & ( 1<< i ) )!= 0);
				}
				currentSequence++;
				return output;
				
			}
		}

	}
	private  int firstVisibleLine;



    private final int signalsInOneSequence = 3;
    
    private final int numberOfVisibleRows = 8;
    private final int numberOfRows = 32;
    private final int sequenceSize = numberOfRows * signalsInOneSequence;
    private final int visibleSequenceSize = numberOfVisibleRows * signalsInOneSequence;
    private final int timeSlots = 4; 
    private final int fullSize = sequenceSize + numberOfRows + timeSlots;
    
    private final int visibleSize =  visibleSequenceSize + numberOfVisibleRows + timeSlots;
    

    private final int sequnceIndexSlots = numberOfRows;
    
    private final int sequenceEnd = sequenceSize;
    private final int sequnceIndexEnd = sequenceEnd + sequnceIndexSlots;
    private final int timeEnd = sequnceIndexEnd + timeSlots;
    
    int scrolingStartRow;
    
    private int currentSequence = 0;
    private final int storagePacketEntitySize = 4;
    private ArrayList< boolean[] > sequence = new ArrayList< boolean[] >();
    
    private boolean  powered;
    public TileController()
    {
    	lines = new Line[numberOfRows];
       // sequencerVisibleContent = new ItemStack[visibleSize];
        scrolingStartRow = 0;

    }

    public void setPower( boolean newPowerState )
    {
    	if (
    			powered == false
    	&&     newPowerState == true
    		)
    		resetCounter();
    	
    		powered = newPowerState;
    	
    }

    public boolean getPower()
    {
    	return powered;
    	
    }
    
    public ItemStack getStackInSlotOnClosing(int par1)
    {
    	
    	ItemStack itemstack  = getStackInSlot(par1);
    	setIntoSequence(par1,null);
    	return itemstack;

    }

    public  void executeLine()
    {
		//if( currentLine.isJump() )
			//else
    }	

    
    public int getSizeInventory()
    {
        return fullSize;
    }

    public  void  resetCounter()
    {
        
        currentSequence = 0;
    }
    
    public ItemStack getStackInSlot(int i)
    {
    	if( sizeOfLine()* numberOfVisibleRows <= i)
    	{
    		if( frequencySection == null )
    			return null;
    	
    		return frequencySection.getItemStack(i - sizeOfLine()* numberOfVisibleRows);	

    	}
    	
    	
    	i+=scrolingStartRow* sizeOfLine();
    	if(sizeOfLines() > i )
    	{
    	
	    	Line line =  lines[i / sizeOfLine()];
	    	if (line == null)
	    			return null; 
	    	return line.getStack(i%sizeOfLine());
    	
    	}

    	return null;
    }

    public ItemStack decrStackSize(int i, int j)
    {
    	
    	
    	if( sizeOfLine()* numberOfVisibleRows <= i)
    	{
    		if( frequencySection == null )
    			return null;
    	
    		return frequencySection.decrementStackSize(i - sizeOfLine()* numberOfVisibleRows,j);	

    	}
    	i+= scrolingStartRow* sizeOfLine();
    	if( sizeOfLines() > i )
    	{
    		return lines[i / sizeOfLine()].decrementStackSize(i % sizeOfLine(), j);
    	}
    	  return  null;

    }

    
    private void updateSequenceArray()
    {

    }
    
    public  boolean [] getNextSequence( int input )
    {
    	if (lines.length <= currentSequence )
    			return null;
    	if( lines[currentSequence] == null )
    	{	
    		++currentSequence;
    		return  null;
    	}
    		
    	return lines[currentSequence].execute(input);
       
    }
    
    public int getFrequency()
    {
        int frequency = 0;
        for( int i = 0; i < timeSlots ; ++i )
        {
           /*     if(sequencerContent[sequnceIndexEnd + i] == null)
                        continue;
                if(sequencerContent[sequnceIndexEnd + i].itemID == Item.redstone.shiftedIndex)
                {
                        time += 1 << i;
                }*/
        }
        return frequency;
    }
    
    
    public void  inventoryScroled( int  currentFirstRow )
    {
        scrolingStartRow = currentFirstRow;
        //updateAfterScroll();
        
    }
    
    private  int  sizeOfLines()
    {
    	return numberOfRows * (sizeOfInputOutput + sizeOfJump);
    	
    }
    private  int  sizeOfLine()
    {
    	return sizeOfInputOutput + sizeOfJump;
    	
    }    
    private  void setItemStack(int position, ItemStack itemstack)
    {
    	if( sizeOfLine()* numberOfVisibleRows <= position)
    	{
	    	if( frequencySection == null )
	    		frequencySection = new FrequencySection();
	    	frequencySection.setItemStack( position - sizeOfLine()* numberOfVisibleRows, itemstack );
	 
	    	return;	
    	}
    		
    		
    	position+= scrolingStartRow* sizeOfLine();
    	if(sizeOfLines() > position)
    	{
    		Line line =  lines[position / sizeOfLine()];
    		if (line == null)
    			line = new Line(); 
    		line.setStack(position%sizeOfLine(), itemstack);
    		lines[position / sizeOfLine()] = line;
    		return;
    	}

    	/*
        if(setIntoSequence( position, itemstack ))
                return;
        
        if(setIntoSequenceIndex( position, itemstack ))
                return;
        */

        assert(false);
    }
    
    
    private void rewriteLoop ( ItemStack[] source, int size,ItemStack[] target, int start )
    {
        for( int i = 0 ; i < size ; ++i  )
        {
           //     target[start+ i] = source[ i ];

        }  
    }
   /* 

    */
    private boolean  setIntoSequence( int position, ItemStack itemstack )
    {
        
        int  potentialPosition = scrolingStartRow * signalsInOneSequence + position; 
        if(position >= visibleSequenceSize)
                return false;
        
       // sequencerContent[potentialPosition] = itemstack;
        return true;
    }
    
    private boolean  setIntoSequenceIndex( int position, ItemStack itemstack )
    {

        int  potentialPosition =  scrolingStartRow + position - visibleSequenceSize; 
        if( position >= visibleSequenceSize +  numberOfVisibleRows )
                return false;
        
        if(potentialPosition < 0)
                return  false;
        
      //  sequencerContent[potentialPosition+ sequenceEnd] = itemstack;
        return true;
    }   
    

    private  ItemStack[] getValidSequence()
    {
        ItemStack[] sequence = new ItemStack[ visibleSequenceSize ];
        
        int offset = scrolingStartRow * signalsInOneSequence;
        for(  int i = 0;i < visibleSequenceSize  ;++i)
        {
               // sequence[ i ] = sequencerContent[offset + i];
                
        }
        return sequence;
    }
 
    private  ItemStack[]  getValidSequenceIndex()
    {
        ItemStack[] indexes = new ItemStack[ numberOfVisibleRows ];
        int offset =  sequenceEnd+scrolingStartRow;
        for(  int i = 0;i < numberOfVisibleRows  ;++i)
        {
             //   indexes[ i ] = sequencerContent[offset +i];
                
        }
        return indexes;
    }    
    
    private ItemStack[]  getValidTime()
    {
        ItemStack[]  times = new ItemStack[ timeSlots ];
        
        int offset = sequnceIndexEnd;
        for(  int i = 0;i < timeSlots  ;++i)
        {
             //   times[ i ] = sequencerContent[offset + i];
                
        }
        return times;
    }     
    
    public void setInventorySlotContents(int i, ItemStack itemstack)
    {
        setItemStack(i, itemstack);
        //sequencerVisibleContent[i] = itemstack;
        if (itemstack != null && itemstack.stackSize > getInventoryStackLimit())
        {
            itemstack.stackSize = getInventoryStackLimit();
        }
        onInventoryChanged();
    }

    public String getInvName()
    {
        return "Sequenzer";
    }

    private void  loadLines( NBTTagList nbttaglist)
    {
    	NBTTagCompound tagCompound;
    	for ( int  i = 0 ;i < nbttaglist.tagCount() ; i++ )
    	{
    		tagCompound = (NBTTagCompound)nbttaglist.tagAt(i);
            int j = tagCompound.getByte("Slot") & 0xff;
            
            if (j >= 0/* && j < tagCompound.length*/)
            {
            	setItemStack(j,ItemStack.loadItemStackFromNBT(tagCompound));

            }
    		
    	}
    	
    }
    //setItemStack(int position, ItemStack itemstack)
    private NBTTagList  saveLines()
    {
    	
    	NBTTagList nbttaglist = new NBTTagList();
    	for ( int  i = 0 ;i < sizeOfLines() +  sizeOfFrequency; i++ )
    	{
    		if(getStackInSlot(i) != null)
    		{
	            NBTTagCompound nbttagcompound1 = new NBTTagCompound();
	            nbttagcompound1.setByte("Slot", (byte)i);
	            getStackInSlot(i).writeToNBT(nbttagcompound1);
	            nbttaglist.appendTag(nbttagcompound1);
    		}
    	}
    	return nbttaglist;
    }    
    
    
    public void readFromNBT(NBTTagCompound nbttagcompound)
    {
        super.readFromNBT(nbttagcompound);
        NBTTagList nbttaglist = nbttagcompound.getTagList("Items");
        
        loadLines( nbttaglist );
        currentSequence = (int)nbttagcompound.getShort("CurrentSequence");
    }

    public void writeToNBT(NBTTagCompound nbttagcompound)
    {
        super.writeToNBT(nbttagcompound);
        NBTTagList nbttaglist = saveLines();
        
        nbttagcompound.setTag("Items", nbttaglist);
        nbttagcompound.setShort("CurrentSequence", (short)currentSequence);
    }
    
    public static  int[] concat(int[] first, int[] second) 
    {
          int[] result = Arrays.copyOf(first, first.length + second.length);
          System.arraycopy(second, 0, result, first.length, second.length);
          return result;
    }
    
    private void addItemStackTopacket( ItemStack  stack, int idNumber,Packet230ModLoader packet )
    {
                if( stack == null )
                        return;
                int  itemArray[] = new int [storagePacketEntitySize];
                itemArray[0] = idNumber;
                itemArray[1] = stack.itemID;
                itemArray[2] = stack.stackSize;
                itemArray[3] = stack.getItemDamage();
                packet.dataInt = concat(packet.dataInt, itemArray);
    }


    public void loadFromPacket( Packet230ModLoader packet )
    {
    /*
        int  contentAreaLength = packet.dataInt.length - 3;

        
        if ( contentAreaLength  <= 0 )
                return;
        assert( contentAreaLength % storagePacketEntitySize  == 0 );
        int  ptr  = 3;
        for ( int i =  0; i < sequencerContent.length; i ++)
        {
                if( ptr >=  packet.dataInt.length )
                {
                        sequencerContent[i]= null;
                        continue;
                }       
                if( 
                                   packet.dataInt[ptr+ 0] == i  
                                || packet.dataInt.length  <= ptr
                  )
                {
                        sequencerContent[i]= null;
                        continue;
                }
                
                        ItemStack  itemStack  =  new  ItemStack(packet.dataInt[ptr+ 1],packet.dataInt[ptr+ 2],packet.dataInt[ptr+ 3]);

                        
                        sequencerContent[i] = itemStack;
                        ptr+=storagePacketEntitySize;
                }*/
    }

    public void saveSlider( Packet230ModLoader packet )
    {   
        packet.packetType = mod_AsPj.idSequencerSliderPacket;
        int [] integers =  new int [4];
        integers[0] = xCoord;
                integers[1] = yCoord;
                integers[2] = zCoord;
        integers[3] = scrolingStartRow;
        packet.dataInt = integers;
    }
    
    public void saveIntoPacket( Packet230ModLoader packet )
    {   
        
      /*          packet.packetType = mod_AsPj.idSequencerPacket;
                int [] coords =  new int [3];
                
                coords[0] = xCoord;
                coords[1] = yCoord;
                coords[2] = zCoord;
                packet.dataInt = coords;
                for(int i = 0; i < sequencerContent.length; i++)
                {               
                        addItemStackTopacket( sequencerContent[i],i,packet );
                }*/
    }


    public int getInventoryStackLimit()
    {
        return 2;
    }
    
    
    
    public boolean isUseableByPlayer(EntityPlayer entityplayer)
    {
        if (worldObj.getBlockTileEntity(xCoord, yCoord, zCoord) != this)
        {
            return false;
        }
        return entityplayer.getDistanceSq((double)xCoord + 0.5D, (double)yCoord + 0.5D, (double)zCoord + 0.5D) <= 64D;
    }
    
    public void openChest()
    {
    }

    public void closeChest()
    {
    }
}
